package com.briup.smartcity_group1.service.impl;

import com.briup.smartcity_group1.bean.basic.BaseUser;
import com.briup.smartcity_group1.consts.TokenColumnNameConsts;
import com.briup.smartcity_group1.consts.UserRoleConsts;
import com.briup.smartcity_group1.consts.UserStatusConsts;
import com.briup.smartcity_group1.context.TokenContext;
import com.briup.smartcity_group1.dao.ext.BaseUserExtMapper;
import com.briup.smartcity_group1.exception.ServiceException;
import com.briup.smartcity_group1.service.ILoginService;
import com.briup.smartcity_group1.utils.CheckUtils;
import com.briup.smartcity_group1.utils.JwtUtils;
import com.briup.smartcity_group1.utils.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/** 登录模块服务实现
 * @author congee(congee02 @ 163.com)
 * @date 1/4/2023 2:41 PM
 */
@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private BaseUserExtMapper extMapper;

    @Resource(type = Set.class, name = "tokenBlackList")
    private Set<String> tokenBlackList;

    /**
     * 用户登录接口,返回token字符串
     * <p>
     * 要求使用 {@link JwtUtils#sign(String, Map)} 来完成的生成。
     * 其中 Map 中必须包含 realname, username, role 三个信息
     * </p>
     *
     * @param username 用户名
     * @param password 密码
     * @return JWT token字符串
     * @throws ServiceException 用户名不存在, 密码错误, 账号状态锁定...
     */
    @Override
    public String login(String username, String password) {

        BaseUser byName = extMapper.selectByName(username);

        /* 判断用户受是否存在 */
        if (byName == null) {
            throw new ServiceException(ResultCode.USER_NOT_FOUND);
        }

        /* 若用户存在，判断用户的密码是否匹配 */
        String validatePassword = byName.getPassword();
        if (! validatePassword.equals(password)) {
            throw new ServiceException(ResultCode.USER_LOGIN_PASSWD_ERROR);
        }

        /* 判断用户是否被锁定 */
        Integer status = byName.getStatus();
        if (status == null || status .equals(UserStatusConsts.LOCKED)) {
            throw new ServiceException(ResultCode.USER_LOGIN_LOCKED);
        }

        /* map 通过K-V来存储 realname, username, role */
        HashMap<String, Object> infoMap = new HashMap<>();
        infoMap.put(TokenColumnNameConsts.REALNAME, byName.getRealname());
        infoMap.put(TokenColumnNameConsts.USERNAME, byName.getUsername());
        infoMap.put(TokenColumnNameConsts.ROLE, byName.getRole());

        log.info("用户尝试登录: {}", byName.toString());

        /* 生成 token */
        String token = JwtUtils.sign(byName.getUsername(), infoMap);

        return token;
    }

    /**
     * 用户登出
     * <p>
     * 如果用户在未登入或token失效的情况下登出，则提示用户已经登出
     * </p>
     *
     * @throws ServiceException 用户在没有登录的情况下登出
     */
    @Override
    public void logout() {
        HttpServletRequest request
                = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = request.getHeader("Authorization");
        log.info("尝试登出: {}", token);
        if (token == null || token.isEmpty() || ! JwtUtils.checkSign(token)) {
            throw new ServiceException(ResultCode.USER_LOGOUT_HAVE_LOGGED_OUT);
        }
        // 黑名单若满，则清空
        if (tokenBlackList.size() >= TokenContext.BLACKLIST_MAX_SIZE) {
            tokenBlackList.clear();
        }
        tokenBlackList.add(token);
    }

    /**
     * 用户注册
     * <p>
     * 必填项为 dob, email, gender, icon, password, realname, telephone, username
     * 若用户的邮箱、手机或者用户名已被使用， 密码强度不足， 或者出现不期望的值时，则提示用户更改。
     * 校验的相关方法统一写在 {@link CheckUtils} 中，返回 boolean。
     * </p>
     *
     * @param baseUser 注册信息
     * @throws ServiceException 用户的邮箱、手机或者用户名已被使用 ，密码强度不足， 参数校验错误 或 必填项为空
     */
    @Override
    public void register(BaseUser baseUser) {
        String username = baseUser.getUsername();
        String telephone = baseUser.getTelephone();
        String email = baseUser.getEmail();
        /* 参数校验 */
        if (! CheckUtils.checkUsername(username)) {
            throw new ServiceException(ResultCode.USER_REGISTER_USERNAME_PARAM_ERROR);
        }
        if (! CheckUtils.checkEmail(email)) {
            throw new ServiceException(ResultCode.USER_REGISTER_EMAIL_PARAM_ERROR);
        }
        if (! CheckUtils.checkTelephone(telephone)) {
            throw new ServiceException(ResultCode.USER_REGISTER_TELEPHONE_PARAM_ERROR);
        }

        // 在 controller 中规定 username, telephone, email 为必填项，不会出现 null
        /* 判断 用户名 或 邮箱 或 手机号 是否被注册 */
        BaseUser byNameOrPhoneOrEmail =
                extMapper.selectByNameOrPhoneOrEmail(username, telephone, email);
        if (byNameOrPhoneOrEmail != null) {
            if (extMapper.selectByName(username) != null) {
                throw new ServiceException(ResultCode.USER_REGISTER_DUPLICATE_USERNAME);
            }
            if (extMapper.selectByEmail(email) != null) {
                throw new ServiceException(ResultCode.USER_REGISTER_DUPLICATE_EMAIL);
            }
            if (extMapper.selectByPhone(telephone) != null) {
                throw new ServiceException(ResultCode.USER_REGISTER_DUPLICATE_TELEPHONE);
            }
        }

        /* 为用户设置默认值 */
        baseUser.setUserId(null);
        baseUser.setRegisterTime(new Date());
        baseUser.setRole(UserRoleConsts.ROLE_USER);

    }
}
